package cn.jpanda.elastic.job.autoconfigure.configuration;

import cn.jpanda.elastic.job.autoconfigure.strategy.JobParseAnnotationHandlerStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.CollectionUtils;

import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * 自定义bean扫描器，主要目的是扫描自定义注解
 *
 * @author Hanqi <jpanda@aliyun.com>
 * @since 2018/9/25 9:57
 */
@Slf4j
public class ElasticJobClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
    /**
     * 存放 ElasticJobMeteData注解和JobParseAnnotationHandlerStrategy的对应关系
     */
    private Map<Class<? extends Annotation>, JobParseAnnotationHandlerStrategy> jobParseStrategies;


    public Set<BeanDefinitionHolder> registerSpringScheduler(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
        processRegisterSpringScheduler(beanDefinitionHolders);
        return beanDefinitionHolders;
    }

    protected void processRegisterSpringScheduler(Set<BeanDefinitionHolder> beanDefinitionHolders) {
        if (CollectionUtils.isEmpty(beanDefinitionHolders)) {
            return;
        }
        // 获取注解处理器
        //直接加载初始化作业处理器

        beanDefinitionHolders.forEach(beanDefinitionHolder -> {
            BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();

            // 获取bean注解元数据
            AnnotationMetadata metadata = ((AnnotatedBeanDefinition) beanDefinition).getMetadata();
            Set<String> annotationNames = metadata.getAnnotationTypes();
            jobParseStrategies.forEach((annotation, strategy) -> {
                if (annotationNames.contains(annotation.getCanonicalName())) {
                    // 根据策略初始化SpringJobScheduler
                    BeanDefinition jobScheduler = strategy.getJobScheduler((ScannedGenericBeanDefinition) beanDefinition, metadata);
                    assert this.getRegistry() != null;

                    // 注册到容器内
                    String beanName = getSpringSchedulerName(jobScheduler);
                    this.getRegistry().registerBeanDefinition(beanName, jobScheduler);
                    log.debug("Registry spring job scheduler {} to spring container.", beanName);
                    return;
                }


            });
            metadata.getAnnotationTypes().forEach(type -> {

            });
        });
    }

    public ElasticJobClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
        super(registry, false);
    }

    public Map<Class<? extends Annotation>, JobParseAnnotationHandlerStrategy> getJobParseStrategies() {
        return jobParseStrategies;
    }

    public void setJobParseStrategies(Map<Class<? extends Annotation>, JobParseAnnotationHandlerStrategy> jobParseStrategies) {
        this.jobParseStrategies = jobParseStrategies;
    }

    private String getSpringSchedulerName(BeanDefinition jobScheduler) {
//        ElasticJobAnnotationConstants.DEFAULT_ELASTIC_JOB_SCHEDULER_NAME_PREFIX +
        return((BeanDefinition) (Objects.requireNonNull(Objects.requireNonNull(jobScheduler.getConstructorArgumentValues().getArgumentValue(0, BeanDefinition.class)).getValue()))).getBeanClassName();
    }
}
